package com.jfinal.ext.util;

import com.jfinal.ext.util.rand.MyUuidUtil;
import com.jfinal.ext.util.rand.RandomStrUtils;
import org.jetbrains.annotations.NotNull;

import java.io.File;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.atomic.AtomicLong;
import java.util.function.IntFunction;
import java.util.function.Supplier;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * @author 江理_网猿 (timor)
 * @date 2020/10/10 0010
 */
@SuppressWarnings("unused")
public enum FormatUtil {;
    private static final String PATTERN_STRING = "\\{([^\\}]+)\\}";
    private static final Pattern PATTERN = Pattern.compile(PATTERN_STRING, Pattern.CASE_INSENSITIVE);

    public static final String FULL_DATE = "yyyy-MM-dd_HH-mm-ss-SSS";
    public static final String COMMON_DATE = "yyyy-MM-dd_HH-mm-ss";

    public static final String DATE = "yyyy-MM-dd";
    public static final String TIME = "HH:mm:ss";
    public static final String DATE_TIME = DATE + " " + TIME;

    public static final String DATE_NICK = "date";
    public static final String TIME_NICK = "time";
    public static final String DATE_TIME_NICK = "datetime";

    public static final String STANDER_DATE_NICK = "s-date";
    public static final String STANDER_TIME_NICK = "s-time";
    public static final String STANDER_DATE_TIME_NICK = "s-datetime";

    public static final String NAME_TIME = "HH-mm-ss";
    public static final String NAME_DATE_TIME = DATE + "_" + NAME_TIME;
    public static final String NAME_DATE_NICK = "n-date";
    public static final String NAME_TIME_NICK = "n-time";
    public static final String NAME_DATE_TIME_NICK = "n-datetime";


    public static final String TIMESTAMP = "timestamp";

    public static final String FULL_YEAR = "yyyy";
    public static final String YEAR = "yy";
    public static final String MONTH = "MM";
    public static final String DAY = "dd";
    public static final String HOUR = "HH";
    public static final String MINUTE = "mm";
    public static final String SECOND = "ss";
    public static final String TIME_MILL = "SS";
    private static final String NANO_TIME = "nanoTime";

    private static final String NEXT_SEQ = "nextSeq";

    private static final String UUID = "uuid";
    /**
     * default: rand_num
     */
    private static final String RAND = "rand";
    private static final String RANDOM_NUM = "randomNum";
    private static final String RANDOM_CHAR = "randomChar";
    private static final String RANDOM_MIX = "randomMix";

    public static final String FILE_PATH = "filePath";
    public static final String FILE_NAME = "fileName";
    public static final String FILE_TYPE = "fileType";

    public static final String PREFIX = "prefix";
    public static final String SUFFIX = "suffix";


    private static final AtomicLong SEQ = new AtomicLong();


    private static final Map<String, Supplier<String>> FORMAT_PATTERN = new HashMap<>();
    private static final Map<String, IntFunction<String>> FORMAT_FUNC = Map.ofEntries(
            Map.entry(RAND, RandomStrUtils::randomNumeric),
            Map.entry(RANDOM_NUM, RandomStrUtils::randomNumeric),
            Map.entry(RANDOM_MIX, RandomStrUtils::randomAlphanumeric),
            Map.entry(RANDOM_CHAR, RandomStrUtils::randomAlphabetic)
    );



    static {
        final String[] patternFlag =
                { FULL_YEAR, YEAR, MONTH, DAY, HOUR, MINUTE, SECOND, TIME_MILL };

        for (String flag : patternFlag) {
            FORMAT_PATTERN.put(flag, () -> (new SimpleDateFormat(flag)).format(new Date()));
        }


        FORMAT_PATTERN.put(FULL_DATE, () -> (new SimpleDateFormat(FULL_DATE)).format(new Date()));
        FORMAT_PATTERN.put(COMMON_DATE, () -> (new SimpleDateFormat(COMMON_DATE)).format(new Date()));

        FORMAT_PATTERN.put(DATE, ()-> (new SimpleDateFormat(DATE)).format(new Date()));
        FORMAT_PATTERN.put(TIME, ()-> (new SimpleDateFormat(TIME)).format(new Date()));
        FORMAT_PATTERN.put(DATE_TIME, ()-> (new SimpleDateFormat(DATE_TIME)).format(new Date()));

        FORMAT_PATTERN.put(DATE_NICK, ()-> (new SimpleDateFormat(DATE)).format(new Date()));
        FORMAT_PATTERN.put(TIME_NICK, ()-> (new SimpleDateFormat(TIME)).format(new Date()));
        FORMAT_PATTERN.put(DATE_TIME_NICK, ()-> (new SimpleDateFormat(DATE_TIME)).format(new Date()));

        FORMAT_PATTERN.put(STANDER_DATE_NICK, ()-> (new SimpleDateFormat(DATE)).format(new Date()));
        FORMAT_PATTERN.put(STANDER_TIME_NICK, ()-> (new SimpleDateFormat(TIME)).format(new Date()));
        FORMAT_PATTERN.put(STANDER_DATE_TIME_NICK, ()-> (new SimpleDateFormat(DATE_TIME)).format(new Date()));

        FORMAT_PATTERN.put(NAME_DATE_NICK, ()-> (new SimpleDateFormat(DATE)).format(new Date()));
        FORMAT_PATTERN.put(NAME_TIME_NICK, ()-> (new SimpleDateFormat(NAME_TIME)).format(new Date()));
        FORMAT_PATTERN.put(NAME_DATE_TIME_NICK, ()-> (new SimpleDateFormat(NAME_DATE_TIME)).format(new Date()));


        FORMAT_PATTERN.put(UUID, MyUuidUtil::get);
        FORMAT_PATTERN.put(NANO_TIME, ()-> String.valueOf(System.nanoTime()));
        FORMAT_PATTERN.put(TIMESTAMP, ()-> String.valueOf(System.currentTimeMillis()));
        FORMAT_PATTERN.put(NEXT_SEQ, FormatUtil::geNetSeq);
    }


    // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -


    /**
     * 格式化路径
     * @param template 模板
     */
    public static String parse(@NotNull final String template) {
        final Matcher matcher = PATTERN.matcher(template);
        final StringBuilder sb = new StringBuilder();

        while(matcher.find()) {
            matcher.appendReplacement(sb, format(matcher.group(1)));
        }

        matcher.appendTail(sb);
        return sb.toString();
    }


    /**
     * 格式化路径
     * @param template 模板
     * @param file 文件
     * @return string || !file.exists() -> template
     */
    public static String parse(@NotNull final String template,
                               @NotNull final File file) {

        if (!file.exists()) {
            return template;
        }


        final Map<String, Object> env = file.isFile()
                ? Map.ofEntries(
                        Map.entry(FILE_PATH, file.getParent()),
                        Map.entry(FILE_NAME, MyFileUtil.getName(file)),
                        Map.entry(FILE_TYPE, MyFileUtil.getExtension(file)))
                : Map.of(FILE_PATH, file.getAbsolutePath());

        return parse(template, env);
    }


    /**
     * 格式化路径
     * @param template 模板
     * @param env 自定义变量
     * @return string
     */
    public static String parse(@NotNull final String template,
                               @NotNull final Map<String, Object> env) {
        final Matcher matcher = PATTERN.matcher(template);
        final StringBuilder sb = new StringBuilder();

        while(matcher.find()) {
            final String temp = matcher.group(1);
            final Object target = env.get(temp);

            final String replacement =
                    null == target ? format(temp) : target.toString();
            matcher.appendReplacement(sb, replacement);
        }

        matcher.appendTail(sb);
        return sb.toString();
    }


    // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -


    private static String format(String string) {
        final Supplier<String> supplier = FORMAT_PATTERN.get(string);

        if(null != supplier){
            return supplier.get();
        }


        final String[] split = string.split(":");
        final String temp = split[0].trim();
        final IntFunction<String> func = FORMAT_FUNC.get(temp);

        if(null != func){
            return func.apply(Integer.parseInt(split[1].trim()));
        }
        return string;
    }


    // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -


    private static String geNetSeq() {
        return String.valueOf(netSeq());
    }

    private static long netSeq() {
        return SEQ.incrementAndGet();
    }

}
